iT邦幫忙

2022 iThome 鐵人賽

DAY 11
0
Software Development

小青蛇變大蟒蛇——進階Python學起來!系列 第 11

Python 與浮點數(float):比較浮點數

  • 分享至 

  • xImage
  •  

昨天我們發現浮點數常是一個逼近值,因此有時候比較是否相等的結果會令你出乎意料。

那麼,今天就來研究,我們要怎麼克服這個「相等」的問題!

回憶一下昨天發現的怪現象:

x = 0.1 + 0.1 + 0.1
y = 0.3
x == y
False

這是因為 0.10.3 其實是逼近值,並非完全精確。多看幾個小數點位數可以發現:

print('0.1 --> {0:.25f}'.format(0.1))
print('x --> {0:.25f}'.format(x))
print('y --> {0:.25f}'.format(y))
0.1 --> 0.1000000000000000055511151
x --> 0.3000000000000000444089210
y --> 0.2999999999999999888977698

那有沒有方法克服這個情況呢?一個作法是使用 round 方法,四捨五入到小數點後某個位數再比較:

x = 0.1 + 0.1 + 0.1
y = 0.3
round(x, 5) == round(y, 5)
True

使用 round 讓我們以一個近似值比較是否相等。

但有個問題,假設A、B兩個數字很大:

A = 50000000.00001
B = 50000000.00002

round(A, 5) == round(B, 5)
False

我們發現使用 round 四捨五入到小數點後五位,他們並不相等。

但其實 A, B 是很接近的,因為他們其實是很大的數字,小數點後面五位可以說幾乎不重要,我們可以把兩者相除看看:

print(A / B)
0.9999999999998

幾乎等於 1,由於這兩個浮點數本來就是近似值,我們可能希望將它們兩個視為「相等」。

那怎麼辦呢?我們來介紹一個在 math 模組裡面的 isclose 方法:

from math import isclose

help(isclose)
Help on built-in function isclose in module math:

isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0)
    Determine whether two floating point numbers are close in value.
    
      rel_tol
        maximum difference for being considered "close", relative to the
        magnitude of the input values
      abs_tol
        maximum difference for being considered "close", regardless of the
        magnitude of the input values
    
    Return True if a is close in value to b, and False otherwise.
    
    For the values to be considered close, the difference between them
    must be smaller than at least one of the tolerances.
    
    -inf, inf and NaN behave similarly to the IEEE 754 Standard.  That
    is, NaN is not close to anything, even itself.  inf and -inf are
    only close to themselves.
x = 0.1 + 0.1 + 0.1
y = 0.3
isclose(x, y)
True

isclose 方法有兩個引數: rel_tolabs_tol.

rel_tol 是指A、B兩個數字的相對差異,小於這個差異可視為兩數接近(close),預設值是 A, B 較大者乘上 1e-09。
abs_tol 是指A、B兩個數字的絕對差異,跟 A, B 多大多小無關,是一個定值,兩者相減小於此值視為接近,預設值是 0。

看一下以下的例子,x, y 算很接近:

x = 123456789.01
y = 123456789.02

但下面這兩個就差滿多了:

x = 0.01
y = 0.02

上面兩個例子的 x, y 都是相差 0.01,但因為數字本身大小不同使得第一對很接近,第二對有差距。

這時候相對差異就很好用:

isclose(123456789.01, 123456789.02, rel_tol=0.01)
True
isclose(0.01, 0.02, rel_tol=0.01)
False

但是要小心了!當數字很接近零的時候,相對差異永遠微不足道————這使得兩個數字永遠無法「接近」。

x = 0.000000000001
y = 0.0000000000005
isclose(x, y, rel_tol=0.01)
False

上述兩個數字幾乎等於零,我們希望視為兩個數字接近,相對差異已經失效了,那怎麼辦?

絕對差異派上用場!

isclose(x, y, abs_tol=0.0001, rel_tol=0)
True

x, y 兩者相減大於絕對差異的設定 0.0001,因而兩者視為接近。

善用相對差異跟絕對差異的配合,我們就可以做出各種浮點數都適用的「相等」判斷了!

x = 0.0000001
y = 0.0000002

a = 123456789.01
b = 123456789.02

print('x = y:', isclose(x, y, abs_tol=0.0001, rel_tol=0.01))
print('a = b:', isclose(a, b, abs_tol=0.0001, rel_tol=0.01))
x = y: True
a = b: True

明天繼續浮點數之旅,我們明~天~見!

參考:Python 3: Deep Dive (Part 1 - Functional)


上一篇
Python 與浮點數(float):一些小介紹
下一篇
Python 與浮點數(float):簡化浮點數
系列文
小青蛇變大蟒蛇——進階Python學起來!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言